%{
// Copyright 2013 The ql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSES/QL-LICENSE file.

// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

// Inital yacc source generated by ebnf2y[1]
// at 2013-10-04 23:10:47.861401015 +0200 CEST
//
//  $ ebnf2y -o ql.y -oe ql.ebnf -start StatementList -pkg ql -p _
// 
//   [1]: http://github.com/cznic/ebnf2y

package parser

import (
	"fmt"
	"strings"
	
	mysql "github.com/pingcap/tidb/mysqldef"
	"github.com/pingcap/tidb/parser/coldef"
	"github.com/pingcap/tidb/ddl"
	"github.com/pingcap/tidb/expression"
	"github.com/pingcap/tidb/expression/expressions"
	"github.com/pingcap/tidb/field"
	"github.com/pingcap/tidb/model"
	"github.com/pingcap/tidb/parser/opcode"
	"github.com/pingcap/tidb/rset/rsets"
	"github.com/pingcap/tidb/stmt"
	"github.com/pingcap/tidb/stmt/stmts"
	"github.com/pingcap/tidb/table"
	"github.com/pingcap/tidb/util/charset"
	"github.com/pingcap/tidb/util/types"
)

%}

%union {
	line int
	col  int
	item interface{}
	list []interface{}
}

%token	<item>

	/*yy:token "1.%d"   */	floatLit        "floating-point literal"
	/*yy:token "%c"     */	identifier      "identifier"
	/*yy:token "%di"    */	imaginaryLit	"imaginary literal"
	/*yy:token "%d"     */	intLit          "integer literal"
	/*yy:token "\"%c\"" */	stringLit       "string literal"


	add		"ADD"
	after		"AFTER"
	all 		"ALL"
	alter		"ALTER"
	and		"AND"
	andand		"&&"
	andnot		"&^"
	as		"AS"
	asc		"ASC"
	autoIncrement	"AUTO_INCREMENT"
	begin		"BEGIN"
	between		"BETWEEN"
	by		"BY"
	byteType	"BYTE"
	caseKwd		"CASE"
	cast		"CAST"
	character	"CHARACTER"
	charsetKwd	"CHARSET"
	collation	"COLLATE"
	column		"COLUMN"
	columns		"COLUMNS"
	commit		"COMMIT"
	constraint	"CONSTRAINT"
	convert		"CONVERT"
	create		"CREATE"
	cross 		"CROSS"
	database	"DATABASE"
	databases	"DATABASES"
	deallocate	"DEALLOCATE"
	defaultKwd	"DEFAULT"
	delayed		"DELAYED"
	deleteKwd	"DELETE"
	desc		"DESC"
	describe	"DESCRIBE"
	distinct	"DISTINCT"
	div 		"DIV"
	do		"DO"
	drop		"DROP"
	duplicate	"DUPLICATE"
	durationType	"duration"
	elseKwd		"ELSE"
	end		"END"
	engine		"ENGINE"
	engines		"ENGINES"
	eq		"="
	execute		"EXECUTE"
	exists		"EXISTS"
	explain		"EXPLAIN"
	falseKwd	"false"
	first		"FIRST"
	foreign		"FOREIGN"
	forKwd		"FOR"
	from		"FROM"
	full		"FULL"
	fulltext	"FULLTEXT"
	ge		">="
	global		"GLOBAL"
	group		"GROUP"
	having		"HAVING"
	highPriority	"HIGH_PRIORITY"
	ignore		"IGNORE"
	ifKwd		"IF"
	in		"IN"
	index		"INDEX"
	inner 		"INNER"
	insert		"INSERT"
	into		"INTO"
	is		"IS"
	join		"JOIN"
	key		"KEY"
	le		"<="
	left		"LEFT"
	like		"LIKE"
	limit		"LIMIT"
	local		"LOCAL"
	lock		"LOCK"
	lowPriority	"LOW_PRIORITY"
	lsh		"<<"
	mod 		"MOD"
	mode		"MODE"
	names		"NAMES"
	neq		"!="
	neqSynonym	"<>"
	not		"NOT"
	null		"NULL"
	offset		"OFFSET"
	on		"ON"
	or		"OR"
	order		"ORDER"
	oror		"||"
	outer		"OUTER"
	password	"PASSWORD"
	placeholder	"PLACEHOLDER"
	prepare		"PREPARE"
	primary		"PRIMARY"
	quick		"QUICK"
	references	"REFERENCES"
	regexp		"REGEXP"
	right		"RIGHT"
	rlike		"RLIKE"
	rollback	"ROLLBACK"
	rsh		">>"
	runeType	"rune"
	schema		"SCHEMA"
	schemas		"SCHEMAS"
	selectKwd	"SELECT"
	session		"SESSION"
	set		"SET"
	share		"SHARE"
	show		"SHOW"
	signed		"SIGNED"
	start		"START"
	stringType	"string"
	substring	"SUBSTRING"
	sysVar		"SYS_VAR"
	tableKwd	"TABLE"
	tables		"TABLES"
	then		"THEN"
	transaction	"TRANSACTION"
	trueKwd		"true"
	truncate	"TRUNCATE"
	unknown 	"UNKNOWN"
	union		"UNION"
	unique		"UNIQUE"
	unsigned	"UNSIGNED"
	update		"UPDATE"
	use		"USE"
	using		"USING"
	userVar		"USER_VAR"
	value		"VALUE"
	values		"VALUES"
	warnings	"WARNINGS"
	when		"WHEN"
	where		"WHERE"
	xor 		"XOR"
	zerofill	"ZEROFILL"
	
	calcFoundRows	"SQL_CALC_FOUND_ROWS"

	currentTs	"CURRENT_TIMESTAMP"
	localTime	"LOCALTIME"
	localTs		"LOCALTIMESTAMP"
	now		"NOW"
	
	tinyIntType	"TINYINT"
	smallIntType	"SMALLINT"
	mediumIntType	"MEDIUMINT"
	intType		"INT"
	integerType	"INTEGER"
	bigIntType	"BIGINT"
	bitType		"BIT"
	
	decimalType	"DECIMAL"
	numericType	"NUMERIC"
	floatType	"float"
	doubleType	"DOUBLE"
	precisionType	"PRECISION"
	realType	"REAL"
	
	dateType	"DATE"
	timeType	"TIME"
	datetimeType	"DATETIME"
	timestampType	"TIMESTAMP"
	yearType	"YEAR"
	
	charType	"CHAR"
	varcharType	"VARCHAR"
	binaryType	"BINARY"
	varbinaryType	"VARBINARY"
	tinyblobType	"TINYBLOB"
	blobType	"BLOB"
	mediumblobType	"MEDIUMBLOB"
	longblobType	"LONGBLOB"
	tinytextType	"TINYTEXT"
	textType	"TEXT"
	mediumtextType	"MEDIUMTEXT"
	longtextType	"LONGTEXT"
	
	int16Type	"int16"
	int24Type	"int24"
	int32Type	"int32"
	int64Type	"int64"
	int8Type	"int8"
	uintType	"uint"
	uint16Type	"uint16"
	uint32Type	"uint32"
	uint64Type	"uint64"
	uint8Type	"uint8",
	float32Type	"float32"
	float64Type	"float64"
	bigRatType	"bigrat"
	boolType	"BOOL"
	booleanType	"BOOLEAN"

	parseExpression	"parse expression prefix"

%type   <item>
	AggAllOpt		"All option in aggregate function"
	AlterTableStmt		"Alter table statement"
	AlterSpecification	"Alter table specification"
	AlterSpecificationList	"Alter table specification list"
	AsOpt			"as optional"
	Assignment		"assignment"
	AssignmentList		"assignment list"
	AssignmentList1		"assignment list optional trailing comma"
	AuthString		"Password string value"
	BeginTransactionStmt	"BEGIN TRANSACTION statement"
	CastType		"Cast function target type"
	CharsetName		"Charset Name"
	CollationName		"Collation Name"
	ColumnDef		"table column definition"
	ColumnName		"column name"
	ColumnNameList		"column name list"
	ColumnNameList1		"column name list with optional trailing comma"
	ColumnKeywordOpt	"Column keyword or empty"
	ColumnSetValue		"insert statement set value by column name"
	ColumnSetValueList	"insert statement set value by column name list"
	CommaOpt		"optional comma"
	CommitStmt		"COMMIT statement"
	Constraint		"column value constraint"
	ConstraintElem		"table define constraint element"
	ConstraintKeywordOpt	"Constraint Keyword or empty"
	ConstraintOpt		"optional column value constraint"
	ConstraintOpts		"optional column value constraints"
	CreateDatabase		"Create {DATABASE | SCHEMA}"
	CreateDatabaseStmt	"Create Database Statement"
	CreateIndexStmt		"CREATE INDEX statement"
	CreateIndexStmtUnique	"CREATE INDEX optional UNIQUE clause"
	CreateSpecification	"CREATE Database specification"
	CreateSpecificationList	"CREATE Database specification list"
	CreateTableStmt		"CREATE TABLE statement"
	CrossOpt		"Cross join option"
	DBName			"Database Name"
	DeallocateSym		"Deallocate or drop"
	DeallocateStmt		"Deallocate prepared statement"
	Default			"DEFAULT clause"
	DefaultOpt		"optional DEFAULT clause"
	DefaultKwdOpt		"optional DEFAULT keyword"
	DefaultValueExpr	"DefaultValueExpr(Now or Signed Literal)"
	DeleteFromStmt		"DELETE FROM statement"
	DoStmt			"Do statement"
	DropDatabaseStmt	"DROP DATABASE statement"
	DropIndexStmt		"DROP INDEX statement"
	DropTableStmt		"DROP TABLE statement"
	EmptyStmt		"empty statement"
	EqOpt			"= or empty"
	EscapedTableRef 	"escaped table reference"
	ExecuteStmt		"Execute statement"
	ExplainSym		"EXPLAIN or DESCRIBE or DESC"
	ExplainStmt		"EXPLAIN statement"
	Expression		"expression"
	ExpressionList		"expression list"
	ExpressionListList	"expression list list"
	ExpressionList1		"expression list expression"
	Factor			"expression factor"
	Factor1			"binary expression factor"
	Field			"field expression"
	Field1			"field expression optional AS clause"
	FieldList		"field expression list"
	ForUserOpt		"Set password for user option"
	FromClause		"From clause"
	Function		"function expr"
	FunctionCall		"function call post part"
	FunctionCallArgList	"function call optional argument list"
	GroupByClause		"GROUP BY clause"
	GroupByList		"GROUP BY list"
	HavingClause		"HAVING clause"
	IfExists		"If Exists"
	IfNotExists		"If Not Exists"
	IgnoreOptional		"IGNORE or empty"
	IndexColName		"Index column name"
	IndexColNameList	"List of index column name"
	IndexName		"index name"
	IndexType		"index type"
	InsertIntoStmt		"INSERT INTO statement"
	InsertRest		"Rest part of INSERT INTO statement"
	IntoOpt			"INTO or EmptyString"
	JoinTable 		"join table"
	JoinType		"join type"
	KeyOrIndex		"{KEY|INDEX}"
	LimitClause		"LIMIT clause"
	Literal			"literal value"
	logAnd			"logical and operator"
	logOr			"logical or operator"
	LowPriorityOptional	"LOW_PRIORITY or empty"
	name			"name"
	NotOpt			"optional NOT"
	NowSym			"CURRENT_TIMESTAMP/LOCALTIME/LOCALTIMESTAMP/NOW"
	NumLiteral		"Num/Int/Float/Decimal Literal"
	OnDuplicateKeyUpdate	"ON DUPLICATE KEY UPDATE value list"
	Operand			"operand"
	OptFull			"Full or empty"
	OptInteger		"Optional Integer keyword"
	Order			"ORDER BY clause optional collation specification"
	OrderBy			"ORDER BY clause"
	OrderByItem		"ORDER BY list item"
	OrderByOptional		"Optional ORDER BY clause optional"
	OrderByList 		"ORDER BY list"
	OuterOpt		"optional OUTER clause"
	QualifiedIdent		"qualified identifier"
	QuickOptional		"QUICK or empty"
	PasswordOpt		"Password option"
	ColumnPosition		"Column position [First|After ColumnName]"
	PreparedStmt		"PreparedStmt"
	PrepareSQL		"Prepare statement sql string"
	PrimaryExpression	"primary expression"
	PrimaryFactor		"primary expression factor"
	Priority		"insert statement priority"
	ReferDef		"Reference definition"
	RegexpSym		"REGEXP or RLIKE"
	RollbackStmt		"ROLLBACK statement"
	SelectLockOpt		"FOR UPDATE or LOCK IN SHARE MODE,"
	SelectStmt		"SELECT statement"
	SelectStmtCalcFoundRows	"SELECT statement optional SQL_CALC_FOUND_ROWS"
	SelectStmtDistinct	"SELECT statement optional DISTINCT clause"
	SelectStmtFieldList	"SELECT statement field list"
	SelectStmtLimit		"SELECT statement optional LIMIT clause"
	SelectStmtOpts		"Select statement options"
	SelectStmtWhere		"SELECT statement optional WHERE clause"
	SelectStmtGroup		"SELECT statement optional GROUP BY clause"
	SelectStmtOrder		"SELECT statement optional ORDER BY clause"
	SetStmt			"Set variable statement"
	ShowStmt		"Show engines/databases/tables/columns/warnings statement"
	ShowDatabaseNameOpt	"Show tables/columns statement database name option"
	ShowTableIdentOpt	"Show columns statement table name option"
	SignedLiteral		"Literal or NumLiteral with sign"
	Statement		"statement"
	StatementList		"statement list"
	SubSelect		"Sub Select"
	Symbol			"Constraint Symbol"
	SystemVariable		"System defined variable name"
	TableConstraint		"table constraint definition"
	TableElement		"table definition element"
	TableElementList	"table definition element list"
	TableFactor 		"table factor"
	TableIdent		"Table identifier"
	TableIdentList		"Table identifier list"
	TableIdentOpt 		"Table identifier option"
	TableOpt		"create table option"
	TableOpts		"create table option list"
	TableRef 		"table reference"
	TableRefs 		"table references"
	TruncateTableStmt	"TRANSACTION TABLE statement"
	UnionOpt		"Union Option(empty/ALL/DISTINCT)"
	UnionStmt		"Union statement"
	UpdateStmt		"UPDATE statement"
	Username		"Username"
	UserVariable		"User defined variable name"
	UserVariableList	"User defined variable name list"
	UseStmt			"USE statement"
	ValueSym		"Value or Values"
	VariableAssignment	"set variable value"
	VariableAssignmentList	"set variable value list"
	Variable		"User or system variable"
	WhereClause		"WHERE clause"
	WhereClauseOptional	"Optinal WHERE clause"

	Identifier		"identifier or unreserved keyword"
	UnReservedKeyword	"MySQL unreserved keywords"

	WhenClause		"When clause"
	WhenClauseList		"When clause list"
	ElseOpt			"Optional else clause"
	ExpressionOpt		"Optional expression"

	Type			"Types"

	NumericType		"Numeric types"
	IntegerType		"Integer Types types"
	FixedPointType		"Exact value types"
	FloatingPointType	"Approximate value types"
	BitValueType		"bit value types"

	StringType		"String types"
	BlobType		"Blob types"
	TextType		"Text types"

	DateAndTimeType		"Date and Time types"

	OptFieldLen		"Field length or empty"
	FieldLen		"Field length"
	FieldOpts		"Field type definition option list"
	FieldOpt		"Field type definition option"
	FloatOpt		"Floating-point type option"
	Precision		"Floating-point precision option"
	OptBinary		"Optional BINARY"
	CharsetKw		"charset or charater set"
	OptCharset		"Optional Character setting"
	OptCollate		"Optional Collate setting"
	NUM			"numbers"
	LengthNum		"Field length num(uint64)"

	FunctionNameConflict	"Built-in function call names which are conflict with keywords"


%token	tableRefPriority

%left   join inner cross left right full
/* A dummy token to force the priority of TableRef production in a join. */
%left   tableRefPriority
%left 	oror or
%left 	xor
%left 	andand and
%left 	between
%left 	eq ge le neq neqSynonym '>' '<' is like in
%left 	'|'
%left 	'&'
%left 	rsh lsh
%left 	'-' '+'
%left 	'*' '/' '%' div mod
%left 	'^'
%left 	'~' neg
%right 	not

%start	Start

%%

Start:
	StatementList
|	parseExpression Expression
	{
		yylex.(*lexer).expr = expressions.Expr($2)
	}

/**************************************AlterTableStmt***************************************
 * See: https://dev.mysql.com/doc/refman/5.7/en/alter-table.html
 *******************************************************************************************/
AlterTableStmt:
	"ALTER" IgnoreOptional "TABLE" TableIdent AlterSpecificationList
	{
		$$ = &stmts.AlterTableStmt{
			Ident: $4.(table.Ident),
			Specs: $5.([]*ddl.AlterSpecification),
		}
	}

AlterSpecification:
	TableOpts
	{
		$$ = &ddl.AlterSpecification{
			Action: ddl.AlterTableOpt,
			TableOpts: $1.([]*coldef.TableOpt),
		}
	}
|	"ADD" ColumnKeywordOpt ColumnDef ColumnPosition
	{
		$$ = &ddl.AlterSpecification{
			Action: ddl.AlterAddColumn,
			Column: $3.(*coldef.ColumnDef),
			Position: $4.(*ddl.ColumnPosition),
		}
	}	
|	"ADD" ConstraintKeywordOpt ConstraintElem 
	{
		constraint := $3.(*coldef.TableConstraint)
		if $2 != nil { 
			constraint.ConstrName = $2.(string)
		}
		$$ = &ddl.AlterSpecification{
			Action: ddl.AlterAddConstr,
			Constraint: constraint,
		}
	}
|	"DROP" ColumnKeywordOpt ColumnName
	{
		$$ = &ddl.AlterSpecification{
			Action: ddl.AlterDropColumn,
			Name: $3.(string),
		}
	}
|	"DROP" "PRIMARY" "KEY"
	{
		$$ = &ddl.AlterSpecification{Action: ddl.AlterDropPrimaryKey}
	}
|	"DROP" KeyOrIndex IndexName
	{
		$$ = &ddl.AlterSpecification{
			Action: ddl.AlterDropIndex,
			Name: $3.(string),
		}
	}
|	"DROP" "FOREIGN" "KEY" Symbol
	{
		$$ = &ddl.AlterSpecification{
			Action: ddl.AlterDropForeignKey,
			Name: $4.(string),
		}
	}

KeyOrIndex:
	"KEY"|"INDEX"

ColumnKeywordOpt:
	{}
|	"COLUMN"

ColumnPosition:
	{
		$$ = &ddl.ColumnPosition{Type: ddl.ColumnPositionNone}
	}	
|	"FIRST"
	{
		$$ = &ddl.ColumnPosition{Type: ddl.ColumnPositionFirst}
	}
|	"AFTER" ColumnName
	{
		$$ = &ddl.ColumnPosition{
			Type: ddl.ColumnPositionAfter,
			RelativeColumn: $2.(string),
		}
	}


AlterSpecificationList:
	{
		$$ = []*ddl.AlterSpecification{}
	}
|	AlterSpecification
	{
		$$ = []*ddl.AlterSpecification{$1.(*ddl.AlterSpecification)}
	}
|	AlterSpecificationList ',' AlterSpecification
	{
		$$ = append($1.([]*ddl.AlterSpecification), $3.(*ddl.AlterSpecification))
	}

ConstraintKeywordOpt:
	{
		$$ = nil
	}
|	"CONSTRAINT" 
	{
		$$ = nil
	}
|	"CONSTRAINT" Symbol 
	{
		$$ = $2.(string)
	}

Symbol:
	Identifier

/*******************************************************************************************/
Assignment:
	ColumnName eq Expression
	{
		$$ = expressions.Assignment{ColName: $1.(string), Expr: expressions.Expr($3)}
	}

AssignmentList:
	Assignment AssignmentList1 CommaOpt
	{
		$$ = append([]expressions.Assignment{$1.(expressions.Assignment)}, $2.([]expressions.Assignment)...)
	}

AssignmentList1:
	/* EMPTY */
	{
		$$ = []expressions.Assignment{}
	}
|	AssignmentList1 ',' Assignment
	{
		$$ = append($1.([]expressions.Assignment), $3.(expressions.Assignment))
	}

BeginTransactionStmt:
	"BEGIN"
	{
		$$ = &stmts.BeginStmt{}
	}
|	"START" "TRANSACTION"
	{
		$$ = &stmts.BeginStmt{}
	}

FunctionCall:
	'(' AggAllOpt FunctionCallArgList ')'
	{
		$$ = []interface{}{false, $3}
	}
|	'(' "DISTINCT" AggAllOpt ExpressionList')'
	{
		/* Distinct must have expression list, can not empty and '*' */
		$$ = []interface{}{true, $4}
	}

AggAllOpt:
	{

	}
|	"ALL"
	{
		/* TODO: not all functions support ALL, so later we will distinguish to handle them */
		/* ALL has nothing to do. */
	}

FunctionCallArgList:
	/* EMPTY */
	{
		$$ = []expression.Expression{}
	}
	/* select count(*) from table */
|	'*'  
	{
		$$ = []expression.Expression{ expressions.Value{Val: expressions.TypeStar("*")} }
	}
|	ExpressionList

ColumnDef:
	ColumnName Type ConstraintOpts
	{
		$$ = &coldef.ColumnDef{Name: $1.(string), Tp: $2.(*types.FieldType), Constraints: $3.([]*coldef.ConstraintOpt)}
	}

ColumnName:
	Identifier

ColumnNameList:
	ColumnName ColumnNameList1 CommaOpt
	{
		$$ = append([]string{$1.(string)}, $2.([]string)...)
	}

ColumnNameList1:
	/* EMPTY */
	{
		$$ = []string{}
	}
|	ColumnNameList1 ',' ColumnName
	{
		$$ = append($1.([]string), $3.(string))
	}

CommitStmt:
	"COMMIT"
	{
		$$ = &stmts.CommitStmt{}
	}

Constraint:
	"NOT" "NULL"
	{
		$$ = &coldef.ConstraintOpt{Tp: coldef.ConstrNotNull, Bvalue: true}
	}
|	"NULL"
	{
		$$ = &coldef.ConstraintOpt{Tp: coldef.ConstrNull, Bvalue: true}
	}
|	"AUTO_INCREMENT"
	{
		$$ = &coldef.ConstraintOpt{Tp: coldef.ConstrAutoIncrement, Bvalue: true}
	}
|	"PRIMARY" "KEY"
	{
		$$ = &coldef.ConstraintOpt{Tp: coldef.ConstrPrimaryKey, Bvalue: true}
	}
|	"UNIQUE"
	{
		$$ = &coldef.ConstraintOpt{Tp: coldef.ConstrUniq, Bvalue: true}
	}
|	"UNIQUE" "KEY"
	{
		$$ = &coldef.ConstraintOpt{Tp: coldef.ConstrUniqKey, Bvalue: true}
	}
|	"DEFAULT" DefaultValueExpr
	{
		$$ = &coldef.ConstraintOpt{Tp: coldef.ConstrDefaultValue, Evalue: $2.(expression.Expression)}
	}
|	"ON" "UPDATE" NowSym
	{
		$$ = &coldef.ConstraintOpt{Tp: coldef.ConstrOnUpdate, Evalue: $3.(expression.Expression)}
	}

ConstraintElem:
	"PRIMARY" "KEY" '(' IndexColNameList ')'
	{
		ce := &coldef.TableConstraint{}
		ce.Tp = coldef.ConstrPrimaryKey
		ce.Keys = $4.([]*coldef.IndexColName)
		$$ = ce
	}
|	"FULLTEXT" "KEY" IndexName '(' IndexColNameList ')'
	{
		$$ = &coldef.TableConstraint{
			Tp:         coldef.ConstrFulltext,
			Keys:       $5.([]*coldef.IndexColName),
			ConstrName:    $3.(string)}
	}
|	"INDEX" IndexName '(' IndexColNameList ')'
	{
		$$ = &coldef.TableConstraint{
			Tp:         coldef.ConstrIndex,
			Keys:       $4.([]*coldef.IndexColName),
			ConstrName:    $2.(string)}
	}
|	"KEY" IndexName '(' IndexColNameList ')'
	{
		$$ = &coldef.TableConstraint{
			Tp:         coldef.ConstrKey,
			Keys:       $4.([]*coldef.IndexColName),
			ConstrName:    $2.(string)}
	}
|	"UNIQUE" IndexName '(' IndexColNameList ')'
	{
		$$ = &coldef.TableConstraint{
			Tp:         coldef.ConstrUniq,
			Keys:       $4.([]*coldef.IndexColName),
			ConstrName:    $2.(string)}
	}
|	"UNIQUE" "INDEX" IndexName '(' IndexColNameList ')'
	{
		$$ = &coldef.TableConstraint{
			Tp:         coldef.ConstrUniqIndex,
			Keys:       $5.([]*coldef.IndexColName),
			ConstrName:    $3.(string)}
	}
|	"UNIQUE" "KEY" IndexName '(' IndexColNameList ')'
	{
		$$ = &coldef.TableConstraint{
			Tp:         coldef.ConstrUniqKey,
			Keys:       $5.([]*coldef.IndexColName),
			ConstrName:    $3.(string)}
	}
|	"FOREIGN" "KEY" IndexName '(' IndexColNameList ')' ReferDef
	{
		$$ = &coldef.TableConstraint{
			Tp: coldef.ConstrForeignKey,
			Keys: $5.([]*coldef.IndexColName),
			ConstrName: $3.(string),
			Refer:	$7.(*coldef.ReferenceDef),
		}		
	}

ReferDef:
	"REFERENCES" TableIdent '(' IndexColNameList ')'
	{
		$$ = &coldef.ReferenceDef{TableIdent: $2.(table.Ident), IndexColNames: $4.([]*coldef.IndexColName)}
	}

/*
 * The DEFAULT clause specifies a default value for a column. 
 * With one exception, the default value must be a constant; 
 * it cannot be a function or an expression. This means, for example, 
 * that you cannot set the default for a date column to be the value of 
 * a function such as NOW() or CURRENT_DATE. The exception is that you 
 * can specify CURRENT_TIMESTAMP as the default for a TIMESTAMP or DATETIME column.
 *
 * See: http://dev.mysql.com/doc/refman/5.7/en/create-table.html
 *      https://github.com/mysql/mysql-server/blob/5.7/sql/sql_yacc.yy#L6832
 */
DefaultValueExpr:
	NowSym
|	SignedLiteral

// TODO: Process other three keywords
NowSym:
	"CURRENT_TIMESTAMP"
	{
		$$ = &expressions.Ident{CIStr: model.NewCIStr("CURRENT_TIMESTAMP")}
	}
|	"LOCALTIME"
|	"LOCALTIMESTAMP"
|	"NOW"

SignedLiteral:
	Literal
	{
		$$ = expressions.Value{Val: $1}
	}
|	'+' NumLiteral
	{
		n := expressions.Value{Val: $2}
		$$ = expressions.NewUnaryOperation(opcode.Plus, n)
	}
|	'-' NumLiteral
	{
		n := expressions.Value{Val: $2}
		$$ = expressions.NewUnaryOperation(opcode.Minus, n)
	}

// TODO: support decimal literal
NumLiteral:
	intLit
|	floatLit
	


ConstraintOpt:
	Constraint

ConstraintOpts:
	{
		$$ = []*coldef.ConstraintOpt{}
	}
|	ConstraintOpt
	{
		$$ = []*coldef.ConstraintOpt{$1.(*coldef.ConstraintOpt)} 
	}
|	ConstraintOpts ConstraintOpt
	{
		if $2 != nil {
			$$ = append($1.([]*coldef.ConstraintOpt), $2.(*coldef.ConstraintOpt))
		} else {
			$$ = $1
		}
	}

CreateIndexStmt:
	"CREATE" CreateIndexStmtUnique "INDEX" Identifier "ON" TableIdent '(' IndexColNameList ')'
	{
		indexName, tableIdent, colNameList := $4.(string), $6.(table.Ident), $8.([]*coldef.IndexColName)
		if strings.EqualFold(indexName, tableIdent.Name.O) {
			yylex.(*lexer).err("", "index name collision: %s", indexName)
			return 1
		}
		for _, colName := range colNameList {
			if indexName == colName.ColumnName {
				yylex.(*lexer).err("", "index name collision: %s", indexName)
				return 1
			}
		}

		$$ = &stmts.CreateIndexStmt{
			Unique: $2.(bool), 
			IndexName: indexName,
			TableIdent: tableIdent,
			IndexColNames: colNameList,
		}
		if yylex.(*lexer).root {
			break
		}
	}

CreateIndexStmtUnique:
	{
		$$ = false
	}
|	"UNIQUE"
	{
		$$ = true
	}

IndexColName:
	ColumnName OptFieldLen Order
	{
		//Order is parsed but just ignored as MySQL did
		$$ = &coldef.IndexColName{ColumnName: $1.(string), Length: $2.(int)}	
	}

IndexColNameList:
	{
		$$ = []*coldef.IndexColName{} 
	}
|	IndexColName
	{
		$$ = []*coldef.IndexColName{$1.(*coldef.IndexColName)}
	}
|	IndexColNameList ',' IndexColName
	{
		$$ = append($1.([]*coldef.IndexColName), $3.(*coldef.IndexColName))
	}



/*******************************************************************
 *
 *  Create Database Statement
 *  CREATE {DATABASE | SCHEMA} [IF NOT EXISTS] db_name
 *      [create_specification] ...
 *
 *  create_specification:
 *      [DEFAULT] CHARACTER SET [=] charset_name
 *    | [DEFAULT] COLLATE [=] collation_name
 *******************************************************************/
CreateDatabaseStmt:
	CreateDatabase IfNotExists DBName CreateSpecificationList 
	{
		opts := $4.([]*coldef.DatabaseOpt)
		//compose charset from x
		var cs, co string
		for _, x := range opts {
			switch x.Tp {
			case coldef.DBOptCharset, coldef.DBOptCollate:
				cs = x.Value
			}
		}

		ok := charset.ValidCharsetAndCollation(cs, co)
		if !ok {
			yylex.(*lexer).err("", "Unknown character set %s or collate %s ", cs, co)
		}
		dbopt := &coldef.CharsetOpt{Chs: cs, Col: co}

		$$ = &stmts.CreateDatabaseStmt{
			IfNotExists:    $2.(bool),
			Name:           $3.(string),
			Opt:            dbopt}

		if yylex.(*lexer).root {
			break
		}
	}

CreateDatabase:
	"CREATE" "DATABASE"
	{
	}
|	"CREATE" "SCHEMA"
	{
	}

DBName:
	Identifier

CreateSpecification:
	DefaultOpt CharsetKw EqOpt CharsetName
	{
		$$ = &coldef.DatabaseOpt{Tp: coldef.DBOptCollate, Value: $4.(string)}
	}
|	DefaultOpt "COLLATE" EqOpt CollationName 
	{
		$$ = &coldef.DatabaseOpt{Tp: coldef.DBOptCollate, Value: $4.(string)}
	} 

CharsetName:
	Identifier
	{
		c := strings.ToLower($1.(string))
			if charset.ValidCharsetAndCollation(c, "") {
			$$ = c
		} else {
			yylex.(*lexer).err("", fmt.Sprintf("Unknown character set: '%s'", $1.(string)))
			return 1
		}
	}

CollationName:
	Identifier

CreateSpecificationList:
	{
		$$ = []*coldef.DatabaseOpt{} 
	}
|	CreateSpecification
	{
		$$ = []*coldef.DatabaseOpt{$1.(*coldef.DatabaseOpt)}
	}
|	CreateSpecificationList CreateSpecification
	{
		$$ = append($1.([]*coldef.DatabaseOpt), $2.(*coldef.DatabaseOpt))
	}

/*******************************************************************
 *
 *  Create Table Statement
 *
 *  Example:
 *      CREATE TABLE Persons
 *      (
 *          P_Id int NOT NULL,
 *          LastName varchar(255) NOT NULL,
 *          FirstName varchar(255),
 *          Address varchar(255),
 *          City varchar(255),
 *          PRIMARY KEY (P_Id)
 *      )
 *******************************************************************/
CreateTableStmt:
	"CREATE" "TABLE" IfNotExists TableIdent '(' TableElementList ')' TableOpts CommaOpt
	{
		tes := $6.([]interface {})
		var columnDefs []*coldef.ColumnDef
		var tableConstraints []*coldef.TableConstraint
		for _, te := range tes {
			switch te := te.(type) {
			case *coldef.ColumnDef:
				columnDefs = append(columnDefs, te)
			case *coldef.TableConstraint:
				tableConstraints = append(tableConstraints, te)
			}
		}
		if len(columnDefs) == 0 {
			yylex.(*lexer).err("", "Column Definition List can't be empty.")
			return 1
		}

		opt := &coldef.TableOption{}
		if $8 != nil {
			for _, o := range $8.([]*coldef.TableOpt) {
				switch o.Tp {
				case coldef.TblOptEngine:
					opt.Engine = o.StrValue
				case coldef.TblOptCharset:
					opt.Charset = o.StrValue
				case coldef.TblOptCollate:
					opt.Collate = o.StrValue
				case coldef.TblOptAutoIncrement:
					opt.AutoIncrement = o.UintValue
				}
			}
		}
		$$ = &stmts.CreateTableStmt{
			Ident:          $4.(table.Ident),
			IfNotExists:    $3.(bool),
			Cols:           columnDefs, 
			Constraints:    tableConstraints,
			Opt:            opt}
	}

Default:
	"DEFAULT" Expression
	{
		$$ = $2
	}

DefaultOpt:
	{
		$$ = nil
	}
|	Default

DefaultKwdOpt:
	{}
|	"DEFAULT"
/******************************************************************
 * Do statement
 * See: https://dev.mysql.com/doc/refman/5.7/en/do.html
 ******************************************************************/
DoStmt:
	"DO" ExpressionList
	{
		$$ = &stmts.DoStmt {
			Exprs: $2.([]expression.Expression),
		}
	}

/*******************************************************************
 *
 *  Delete Statement
 *
 *******************************************************************/
DeleteFromStmt:
	"DELETE" LowPriorityOptional QuickOptional IgnoreOptional "FROM" TableIdent WhereClauseOptional OrderByOptional LimitClause
	{
		// Single Table
		x := &stmts.DeleteStmt{
			TableIdent:    $6.(table.Ident),
			LowPriority:   $2.(bool),
			Quick:         $3.(bool),
			Ignore:        $4.(bool)}
	
		if $7 != nil {
			x.Where = $7.(*rsets.WhereRset).Expr
		}

		if $8 != nil {
			x.Order = $8.(*rsets.OrderByRset)
		}

		if $9 != nil {
			x.Limit = $9.(*rsets.LimitRset)
		}

		$$ = x
		if yylex.(*lexer).root {
			break
		}
	}
|	"DELETE" LowPriorityOptional QuickOptional IgnoreOptional TableIdentList "FROM" TableRefs WhereClauseOptional
	{
		// Multiple Table
		x := &stmts.DeleteStmt{
			LowPriority:	$2.(bool),
			Quick:		$3.(bool),
			Ignore:		$4.(bool),
			MultiTable:	true,
			BeforeFrom:	true,
			TableIdents:	$5.([]table.Ident),	
			Refs:		$7.(*rsets.JoinRset),
		}
		if $8 != nil {
			x.Where = $8.(*rsets.WhereRset).Expr
		}
		$$ = x
		if yylex.(*lexer).root {
			break
		}
	}
|	"DELETE" LowPriorityOptional QuickOptional IgnoreOptional "FROM" TableIdentList "USING" TableRefs WhereClauseOptional
	{
		// Multiple Table
		x := &stmts.DeleteStmt{
			LowPriority:	$2.(bool),
			Quick:		$3.(bool),
			Ignore:		$4.(bool),
			MultiTable:	true,
			TableIdents:	$6.([]table.Ident),	
			Refs:		$8.(*rsets.JoinRset),
		}
		if $9 != nil {
			x.Where = $9.(*rsets.WhereRset).Expr
		}
		$$ = x
		if yylex.(*lexer).root {
			break
		}
	}
	

DropDatabaseStmt:
	"DROP" "DATABASE" IfExists Identifier
	{
		$$ = &stmts.DropDatabaseStmt{IfExists: $3.(bool), Name: $4.(string)}
		if yylex.(*lexer).root {
			break
		}
	}

DropIndexStmt:
	"DROP" "INDEX" IfExists Identifier
	{
		$$ = &stmts.DropIndexStmt{IfExists: $3.(bool), IndexName: $4.(string)}
	}

DropTableStmt:
	"DROP" "TABLE" TableIdentList
	{
		$$ = &stmts.DropTableStmt{TableIdents: $3.([]table.Ident)}
		if yylex.(*lexer).root {
			break
		}
	}
|	"DROP" "TABLE" "IF" "EXISTS" TableIdentList
	{
		$$ = &stmts.DropTableStmt{IfExists: true, TableIdents: $5.([]table.Ident)}
		if yylex.(*lexer).root {
			break
		}
	}

EqOpt:
	{
	}
|	eq
	{
	}

EmptyStmt:
	/* EMPTY */
	{
		$$ = nil
	}

ExplainSym:
	"EXPLAIN"
|	"DESCRIBE"
|	"DESC"

ExplainStmt:
	ExplainSym TableIdent
	{
		$$ = &stmts.ExplainStmt{
			S:&stmts.ShowStmt{
				Target:     stmt.ShowColumns,
				TableIdent:  $2.(table.Ident)},
		}
	}
|	ExplainSym TableIdent ColumnName
	{
		$$ = &stmts.ExplainStmt{
			S:&stmts.ShowStmt{
			Target:     stmt.ShowColumns,
			TableIdent:  $2.(table.Ident),
			ColumnName: $3.(string)},
		}
	}
|	ExplainSym Statement
	{
		$$ = &stmts.ExplainStmt{S:$2.(stmt.Statement)}
	}

LengthNum:
	NUM
	{
		switch v := $1.(type) {
		case int64:
			$$ = uint64(v)
		case uint64:
			$$ = uint64(v)
		}
	}

NUM:
	intLit

Expression:
	Expression logOr Expression %prec oror
	{
		$$ = expressions.NewBinaryOperation(opcode.OrOr, $1.(expression.Expression), $3.(expression.Expression))
	}
|	Expression "XOR" Expression %prec xor
	{
		$$ = expressions.NewBinaryOperation(opcode.LogicXor, $1.(expression.Expression), $3.(expression.Expression))
	}
|	Expression logAnd Expression %prec andand
	{
		$$ = expressions.NewBinaryOperation(opcode.AndAnd, $1.(expression.Expression), $3.(expression.Expression))
	}
|	"NOT" Expression %prec not
	{
		$$ = expressions.NewUnaryOperation(opcode.Not, $2.(expression.Expression))
	}
|	Factor "IS" NotOpt trueKwd %prec is
	{
		$$ = &expressions.IsTruth{Expr:$1.(expression.Expression), Not: $3.(bool), True: int8(1)}
	}
|	Factor "IS" NotOpt falseKwd %prec is
	{
		$$ = &expressions.IsTruth{Expr:$1.(expression.Expression), Not: $3.(bool), True: int8(0)}
	}
|	Factor "IS" NotOpt "UNKNOWN" %prec is
	{
		/* https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#operator_is */
		$$ = &expressions.IsNull{Expr: $1.(expression.Expression), Not: $3.(bool)}
	}
|	Factor


logOr:
	"||"
	{
	}
|	"OR"
	{
	}

logAnd:
	"&&"
	{
	}
|	"AND"
	{
	}

name:
	Identifier

ExpressionList:
	Expression ExpressionList1 CommaOpt
	{
		$$ = append([]expression.Expression{expressions.Expr($1)}, $2.([]expression.Expression)...)
	}

ExpressionList1:
	/* EMPTY */
	{
		$$ = []expression.Expression(nil)
	}
|	ExpressionList1 ',' Expression
	{
		$$ = append($1.([]expression.Expression), expressions.Expr($3))
	}

Factor:
	Factor "IS" NotOpt "NULL" %prec is
	{
		$$ = &expressions.IsNull{Expr: $1.(expression.Expression), Not: $3.(bool)}
	}
|	Factor ">=" Factor1 %prec eq
	{
		$$ = expressions.NewBinaryOperation(opcode.GE, $1.(expression.Expression), $3.(expression.Expression))
	}
|	Factor '>' Factor1 %prec eq
	{
		$$ = expressions.NewBinaryOperation(opcode.GT, $1.(expression.Expression), $3.(expression.Expression))
	}
|	Factor "<=" Factor1 %prec eq
	{
		$$ = expressions.NewBinaryOperation(opcode.LE, $1.(expression.Expression), $3.(expression.Expression))
	}
|	Factor '<' Factor1 %prec eq
	{
		$$ = expressions.NewBinaryOperation(opcode.LT, $1.(expression.Expression), $3.(expression.Expression))
	}
|	Factor "!=" Factor1 %prec eq
	{
		$$ = expressions.NewBinaryOperation(opcode.NE, $1.(expression.Expression), $3.(expression.Expression))
	}
|	Factor "<>" Factor1 %prec eq
	{
		$$ = expressions.NewBinaryOperation(opcode.NE, $1.(expression.Expression), $3.(expression.Expression))
	}
|	Factor "=" Factor1 %prec eq
	{
		$$ = expressions.NewBinaryOperation(opcode.EQ, $1.(expression.Expression), $3.(expression.Expression))
	}
|	Factor1

Factor1:
	PrimaryFactor NotOpt "IN" '(' ExpressionList ')'
	{
		$$ = &expressions.PatternIn{Expr: $1.(expression.Expression), Not: $2.(bool), List: $5.([]expression.Expression)}
	}
|	PrimaryFactor NotOpt "IN" '(' SelectStmt semiOpt ')'
	{
		$$ = &expressions.PatternIn{Expr: $1.(expression.Expression), Not: $2.(bool), Sel: $5.(*stmts.SelectStmt)}
	}
|	PrimaryFactor NotOpt "BETWEEN" PrimaryFactor "AND" Factor1
	{
		var err error
		$$, err = expressions.NewBetween($1.(expression.Expression), $4.(expression.Expression), $6.(expression.Expression), $2.(bool))
		if err != nil {
			yylex.(*lexer).err("", "%v", err)
			return 1
		}
	}
|	PrimaryFactor NotOpt "LIKE" PrimaryExpression
	{
		$$ = &expressions.PatternLike{Expr: $1.(expression.Expression), Pattern: $4.(expression.Expression), Not: $2.(bool)}
	}
|	PrimaryFactor NotOpt RegexpSym PrimaryExpression
	{
		$$ = &expressions.PatternRegexp{Expr: $1.(expression.Expression), Pattern: $4.(expression.Expression), Not: $2.(bool)}
	}
|	PrimaryFactor

RegexpSym:
	"REGEXP"
|	"RLIKE"

NotOpt:
	{
		$$ = false
	}
|	"NOT"
	{
		$$ = true
	}

Field:
	'*'
	{
		$$ = &field.Field{Expr: &expressions.Ident{CIStr: model.NewCIStr("*")}}
	}
|	Expression Field1
	{
		expr, name := expressions.Expr($1), $2.(string)
		if name == "" {
			name = expr.String()
		}
		$$ = &field.Field{Expr: expr, Name: name}
	}

Field1:
	/* EMPTY */
	{
		$$ = ""
	}
|	AsOpt
	{
		$$ = $1
	}

AsOpt:
	identifier
	{
		// TODO: check potential bug
		$$ = $1
	}
|	"AS" Identifier
	{
		$$ = $2
	}

FieldList:
	Field
	{
		$$ = []*field.Field{$1.(*field.Field)}
	}
|	FieldList ',' Field
	{
		$$ = append($1.([]*field.Field), $3.(*field.Field))
	}

GroupByClause:
	"GROUP" "BY" GroupByList
	{
		$$ = &rsets.GroupByRset{By: $3.([]expression.Expression)}
	}

GroupByList:
	Expression
	{
		$$ = []expression.Expression{$1.(expression.Expression)}
	}
|	GroupByList ',' Expression
	{
		$$ = append($1.([]expression.Expression), $3.(expression.Expression))
	}

HavingClause:
	{
		$$ = nil
	}
|	"HAVING" Expression
	{
		$$ = &rsets.HavingRset{Expr:$2.(expression.Expression)}
	}

IfExists:
	{
		$$ = false
	}
|	"IF" "EXISTS"
	{
		$$ = true
	}

IfNotExists:
	{
		$$ = false
	}
|	"IF" "NOT" "EXISTS"
	{
		$$ = true
	}


IgnoreOptional:
	{
		$$ = false
	}
|	"IGNORE"
	{
		$$ = true
	}

IndexName:
	{
		$$ = ""
	}
|	Identifier
	{
		//"index name"
		$$ = $1.(string)
	}

IndexType:
	Identifier
	{
		// TODO: "index type"
		$$ = $1.(string)
	}

/**********************************Identifier********************************************/
Identifier:
	identifier | UnReservedKeyword

// TODO: Add Data Type UnReserved Keywords
UnReservedKeyword:
	"AUTO_INCREMENT" | "BEGIN" | "BIT" | "BOOL" | "BOOLEAN" | "CHARSET" | "COLUMN" | "COLUMNS" | "DATE" | "DATETIME"
|	"ENGINE" | "FULL" | "LOCAL" | "NAMES" | "OFFSET" | "PASSWORD" | "QUICK" | "ROLLBACK" | "SESSION" | "GLOBAL" 
|	"TABLES"| "TEXT" | "TIME" | "TIMESTAMP" | "TRANSACTION" | "TRUNCATE" | "VALUE" | "WARNINGS" | "YEAR" | "NOW"
|	"SUBSTRING" | "MODE"


/************************************************************************************
 *
 *  Insert Statments
 *
 *  TODO: support PARTITION
 **********************************************************************************/
InsertIntoStmt:
	"INSERT" Priority IgnoreOptional IntoOpt TableIdent InsertRest OnDuplicateKeyUpdate
	{
		x := $6.(*stmts.InsertIntoStmt)
		x.Priority = $2.(int)
		x.TableIdent = $5.(table.Ident)
		if $7 != nil {
			x.OnDuplicate = $7.([]expressions.Assignment)
		}
		$$ = x
		if yylex.(*lexer).root {
			break
		}
	}

IntoOpt:
	{
	}
|	"INTO"
	{
	}

InsertRest:
	'(' ColumnNameList ')' ValueSym ExpressionListList
	{
		$$ = &stmts.InsertIntoStmt{
			ColNames:   $2.([]string), 
			Lists:      $5.([][]expression.Expression)}
	}
|	'(' ColumnNameList ')' SelectStmt
	{
		$$ = &stmts.InsertIntoStmt{ColNames: $2.([]string), Sel: $4.(*stmts.SelectStmt)}
	}
|	ValueSym ExpressionListList
	{
		$$ = &stmts.InsertIntoStmt{Lists:  $2.([][]expression.Expression)}
	}
|	SelectStmt
	{
		$$ = &stmts.InsertIntoStmt{Sel: $1.(*stmts.SelectStmt)}
	}
|	"SET" ColumnSetValueList
	{
		$$ = &stmts.InsertIntoStmt{Setlist: $2.([]*expressions.Assignment)}
	}

ValueSym:
	"VALUE"
|	"VALUES"

ExpressionListList:
	'(' ')'
	{
		$$ = [][]expression.Expression{[]expression.Expression{}}
	}
|	'(' ')' ',' ExpressionListList
	{
		$$ = append([][]expression.Expression{[]expression.Expression{}}, $4.([][]expression.Expression)...)
	}
|	'(' ExpressionList ')'
	{
		$$ = [][]expression.Expression{$2.([]expression.Expression)}
	}
|	'(' ExpressionList ')' ',' ExpressionListList
	{
		$$ = append([][]expression.Expression{$2.([]expression.Expression)}, $5.([][]expression.Expression)...)
	}

ColumnSetValue:
	ColumnName eq "DEFAULT"
	{
		//set columnname = DEFAULT
		$$ = &expressions.Assignment{
			ColName:    $1.(string),
			Expr:       expressions.Expr($3)}
	}
|	ColumnName eq Expression
	{
		$$ = &expressions.Assignment{
			ColName:    $1.(string),
			Expr:       expressions.Expr($3)}       
	}

ColumnSetValueList:
	{
		$$ = []*expressions.Assignment{}
	}
|	ColumnSetValue
	{
		$$ = []*expressions.Assignment{$1.(*expressions.Assignment)}
	}
|	ColumnSetValueList ',' ColumnSetValue
	{
		$$ = append($1.([]*expressions.Assignment), $3.(*expressions.Assignment))
	}

/* 
 * ON DUPLICATE KEY UPDATE col_name=expr [, col_name=expr] ... 
 * See: https://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html
 */ 
OnDuplicateKeyUpdate:
	{
		$$ = nil
	}
|	"ON" "DUPLICATE" "KEY" "UPDATE" AssignmentList
	{
		$$ = $5
	}

/***********************************Insert Statments END************************************/

Literal:
	"false"
	{
		$$ = int8(0)
	}
|	"NULL"
|	"true"
	{
		$$ = int8(1)
	}
|	floatLit
|	imaginaryLit
|	intLit
|	stringLit

Operand:
	Literal
	{
		$$ = expressions.Value{Val: $1}
	}
|	QualifiedIdent
	{
		$$ = &expressions.Ident{CIStr: model.NewCIStr($1.(string))}
	}
|	'(' Expression ')'
	{
		$$ = &expressions.PExpr{Expr: expressions.Expr($2)}
	}
|	"DEFAULT"
	{
		$$ = &expressions.Default{}
	}
|	"DEFAULT" '(' ColumnName ')'
	{
		$$ = &expressions.Default{Name: $3.(string)}
	}
|	Variable
	{
		$$ = $1
	}
|	"PLACEHOLDER"
	{
		l := yylex.(*lexer)
		if !l.prepare {
			l.err("", "Can not accept placeholder when not parsing prepare sql")
		}
		pm := &expressions.ParamMarker{}	
		l.ParamList = append(l.ParamList, pm)
		$$ = pm
	}

OrderBy:
	"ORDER" "BY" OrderByList
	{
		$$ = &rsets.OrderByRset{By: $3.([]rsets.OrderByItem)}
	}

OrderByList:
	OrderByItem
	{
		$$ = []rsets.OrderByItem{$1.(rsets.OrderByItem)}
	}
|	OrderByList ',' OrderByItem
	{
		$$ = append($1.([]rsets.OrderByItem), $3.(rsets.OrderByItem))
	}

OrderByItem:
	Expression Order 
	{
		$$ = rsets.OrderByItem{Expr: $1.(expression.Expression), Asc: $2.(bool)}
	}

Order:
	/* EMPTY */
	{
		$$ = true // ASC by default
	}
|	"ASC"
	{
		$$ = true
	}
|	"DESC"
	{
		$$ = false
	}

OrderByOptional:
	{
		$$ = nil
	}
|	OrderBy
	{
		$$ = $1
	}
	
PrimaryExpression:
	Operand
|	Function
|	SubSelect
|	'!' PrimaryExpression %prec neg
	{
		$$ = expressions.NewUnaryOperation(opcode.Not, $2.(expression.Expression))
	}
|	'~'  PrimaryExpression %prec neg
	{
		$$ = expressions.NewUnaryOperation(opcode.BitNeg, $2.(expression.Expression))
	}
|	'-' PrimaryExpression %prec neg
	{
		$$ = expressions.NewUnaryOperation(opcode.Minus, $2.(expression.Expression))
	}
|	'+' PrimaryExpression %prec neg
	{
		$$ = expressions.NewUnaryOperation(opcode.Plus, $2.(expression.Expression))
	}

Function:
	PrimaryExpression FunctionCall 
	{
		x := yylex.(*lexer)
		f, ok := $1.(*expressions.Ident)
		if !ok {
			x.err("", "expected identifier or qualified identifier")
			return 1
		}

		var err error
		args := $2.([]interface{})
		if $$, err = expressions.NewCall(f.O, args[1].([]expression.Expression), args[0].(bool)); err != nil {
			x.err("", "%v", err)
			return 1
		}
	}
|	FunctionNameConflict FunctionCall
	{
		x := yylex.(*lexer)
		var err error
		args := $2.([]interface{})
		$$, err = expressions.NewCall($1.(string), args[1].([]expression.Expression), false)
		if err != nil {
			x.err("", "%v", err)
			return 1
		}
	}
|	"CAST" '(' Expression "AS" CastType ')'
	{
		/* See: https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_cast */
		$$ = &expressions.FunctionCast{
			Expr: $3.(expression.Expression), 
			Tp: $5.(*types.FieldType),
		}	
	}
|	"CASE" ExpressionOpt WhenClauseList ElseOpt "END"	
	{
		x := &expressions.FunctionCase{WhenClauses: $3.([]*expressions.WhenClause)}
		if $2 != nil {
			x.Value = $2.(expression.Expression)
		}
		if $4 != nil {
			x.ElseClause = $4.(expression.Expression)
		}
		$$ = x
	}
|	"VALUES" '(' Identifier ')'
	{
		// TODO: support qualified identifier for column_name
		$$ = &expressions.Values{CIStr: model.NewCIStr($3.(string))}
	}
|	"CONVERT" '(' Expression "USING" CharsetName ')' 
	{
		// See: https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert
		$$ = &expressions.FunctionConvert{
			Expr: $3.(expression.Expression), 
			Charset: $5.(string),
		}	
	}
|	"CONVERT" '(' Expression ',' CastType ')' 
	{
		// See: https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert
		$$ = &expressions.FunctionCast{
			Expr: $3.(expression.Expression), 
			Tp: $5.(*types.FieldType),
			IsConvert: true,
		}	
	}
|	"SUBSTRING" '(' Expression ',' Expression ')'
	{
		$$ = &expressions.FunctionSubstring{
			StrExpr: $3.(expression.Expression), 
			Pos: $5.(expression.Expression),
		}	
	}
|	"SUBSTRING" '(' Expression "FROM" Expression ')'
	{
		$$ = &expressions.FunctionSubstring{
			StrExpr: $3.(expression.Expression), 
			Pos: $5.(expression.Expression),
		}	
	}
|	"SUBSTRING" '(' Expression ',' Expression ',' Expression ')'
	{
		$$ = &expressions.FunctionSubstring{
			StrExpr: $3.(expression.Expression), 
			Pos: $5.(expression.Expression),
			Len: $7.(expression.Expression),
		}	
	}
|	"SUBSTRING" '(' Expression "FROM" Expression "FOR" Expression ')'
	{
		$$ = &expressions.FunctionSubstring{
			StrExpr: $3.(expression.Expression), 
			Pos: $5.(expression.Expression),
			Len: $7.(expression.Expression),
		}	
	}


ExpressionOpt:
	{
		$$ = nil
	}
|	Expression
	{	
		$$ = $1
	}

WhenClauseList:
	WhenClause
	{
		$$ = []*expressions.WhenClause{$1.(*expressions.WhenClause)} 			
	}
|	WhenClauseList WhenClause
	{
		$$ = append($1.([]*expressions.WhenClause), $2.(*expressions.WhenClause))
	}

WhenClause:
	"WHEN" Expression "THEN" Expression
	{
		$$ = &expressions.WhenClause{
			Expr: $2.(expression.Expression),
			Result: $4.(expression.Expression),
		}
	}

ElseOpt:
       /* empty */
       {
		$$ = nil	
       }
|	"ELSE" Expression
	{
		$$ = $2
	}

CastType:
	"BINARY" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeString)
		x.Flen = $2.(int) 
		x.Charset = charset.CharsetBin
		x.Collate = charset.CharsetBin
		$$ = x
	}
|	"CHAR" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeString)
		x.Flen = $2.(int) 
		$$ = x
	}
|	"DATE"
	{
		x := types.NewFieldType(mysql.TypeDate)
		$$ = x
	}
|	"DATETIME" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeDatetime)
		x.Decimal = $2.(int)
		$$ = x
	}
|	"DECIMAL" FloatOpt
	{
		fopt := $2.(*coldef.FloatOpt)
		x := types.NewFieldType(mysql.TypeNewDecimal)
		x.Flen = fopt.Flen
		x.Decimal = fopt.Decimal
		$$ = x
	}
|	"TIME" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeDuration)
		x.Decimal = $2.(int)
		$$ = x
	}
|	"SIGNED" OptInteger
	{
		x := types.NewFieldType(mysql.TypeLonglong)
		$$ = x
	}
|	"UNSIGNED" OptInteger
	{
		x := types.NewFieldType(mysql.TypeLonglong)
		x.Flag |= mysql.UnsignedFlag
		$$ = x
	}


FunctionNameConflict:
	"DATABASE"
	{
		$$ = expressions.BuiltinFuncDatabase 
	}
|	"SCHEMA"
	{
		$$ = expressions.BuiltinFuncDatabase 
	}
|	"IF"
	{
		$$ = expressions.BuiltinFuncIf
	}
|	"LEFT"
	{
		$$ = expressions.BuiltinFuncLeft
	}

PrimaryFactor:
	PrimaryFactor '|' PrimaryFactor %prec '|'
	{
		$$ = expressions.NewBinaryOperation(opcode.Or, $1.(expression.Expression), $3.(expression.Expression))
	}
|	PrimaryFactor '&' PrimaryFactor %prec '&'
	{
		$$ = expressions.NewBinaryOperation(opcode.And, $1.(expression.Expression), $3.(expression.Expression))
	}
|	PrimaryFactor "<<" PrimaryFactor %prec lsh
	{
		$$ = expressions.NewBinaryOperation(opcode.LeftShift, $1.(expression.Expression), $3.(expression.Expression))
	}
|	PrimaryFactor ">>" PrimaryFactor %prec rsh
	{
		$$ = expressions.NewBinaryOperation(opcode.RightShift, $1.(expression.Expression), $3.(expression.Expression))
	}
|	PrimaryFactor '+' PrimaryFactor %prec '+'
	{
		$$ = expressions.NewBinaryOperation(opcode.Plus, $1.(expression.Expression), $3.(expression.Expression))
	}
|	PrimaryFactor '-' PrimaryFactor %prec '-'
	{
		$$ = expressions.NewBinaryOperation(opcode.Minus, $1.(expression.Expression), $3.(expression.Expression))
	}
|	PrimaryFactor '*' PrimaryFactor %prec '*'
	{
		$$ = expressions.NewBinaryOperation(opcode.Mul, $1.(expression.Expression), $3.(expression.Expression))
	}
|	PrimaryFactor '/' PrimaryFactor %prec '/'
	{
		$$ = expressions.NewBinaryOperation(opcode.Div, $1.(expression.Expression), $3.(expression.Expression))
	}
|	PrimaryFactor '%' PrimaryFactor %prec '%'
	{
		$$ = expressions.NewBinaryOperation(opcode.Mod, $1.(expression.Expression), $3.(expression.Expression))
	}
|	PrimaryFactor "DIV" PrimaryFactor %prec div
	{
		$$ = expressions.NewBinaryOperation(opcode.IntDiv, $1.(expression.Expression), $3.(expression.Expression))
	}
|	PrimaryFactor "MOD" PrimaryFactor %prec mod
	{
		$$ = expressions.NewBinaryOperation(opcode.Mod, $1.(expression.Expression), $3.(expression.Expression))
	}
|	PrimaryFactor '^' PrimaryFactor
	{
		$$ = expressions.NewBinaryOperation(opcode.Xor, $1.(expression.Expression), $3.(expression.Expression))
	}
|	PrimaryExpression


Priority:
	{
		$$ = stmts.NoPriority
	}
|	"LOW_PRIORITY"
	{
		$$ = stmts.LowPriority
	}
|	"HIGH_PRIORITY"
	{
		$$ = stmts.HighPriority
	}
|	"DELAYED"
	{
		$$ = stmts.DelayedPriority
	}

LowPriorityOptional:
	{
		$$ = false
	}
|	"LOW_PRIORITY"
	{
		$$ = true
	}

QualifiedIdent:
	Identifier
|	Identifier '.' '*'
	{
		$$ = fmt.Sprintf("%s.*", $1.(string))
	}
|	Identifier '.' Identifier
	{
		$$ = fmt.Sprintf("%s.%s", $1.(string), $3.(string))
	}
|	Identifier '.' Identifier '.' Identifier
	{
		$$ = fmt.Sprintf("%s.%s.%s", $1.(string), $3.(string), $5.(string))
	}
|	Identifier '.' Identifier '.' '*'
	{
		$$ = fmt.Sprintf("%s.%s.*", $1.(string), $3.(string))
	}

TableIdent:
	Identifier
	{
		$$ = table.Ident{Name:model.NewCIStr($1.(string))}
	}
|	Identifier '.' Identifier
	{
		$$ = table.Ident{Schema:model.NewCIStr($1.(string)), Name:model.NewCIStr($3.(string))}
	}

TableIdentList:
	{
		$$ = []table.Ident{}
	}
|	TableIdent
	{
		tbl := []table.Ident{$1.(table.Ident)}
		$$ = tbl
	}
|	TableIdentList ',' TableIdent
	{
		$$ = append($1.([]table.Ident), $3.(table.Ident))
	}

QuickOptional:
	{
		$$ = false
	}
|	"QUICK"
	{
		$$ = true
	}

semiOpt:
	/* EMPTY */
|	';'


/***************************Prepared Statement Start******************************
 * See: https://dev.mysql.com/doc/refman/5.7/en/prepare.html
 * Example:
 * PREPARE stmt_name FROM 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse';
 * OR
 * SET @s = 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse';
 * PREPARE stmt_name FROM @s;
 */

PreparedStmt:
	"PREPARE" Identifier "FROM" PrepareSQL
	{
		var sqlText string
		var sqlVar *expressions.Variable
		switch $4.(type) {
		case string:
			sqlText = $4.(string)
		case *expressions.Variable:
			sqlVar = $4.(*expressions.Variable)
		}
		$$ = &stmts.PreparedStmt{
			InPrepare:	true,
			Name:		$2.(string), 
			SQLText:	sqlText, 
			SQLVar: 	sqlVar,
		}
	}

PrepareSQL:
	stringLit
|	UserVariable


/*
 * See: https://dev.mysql.com/doc/refman/5.7/en/execute.html
 * Example:
 * EXECUTE stmt1 USING @a, @b;
 * OR
 * EXECUTE stmt1;
 */
ExecuteStmt:
	"EXECUTE" Identifier
	{
		$$ = &stmts.ExecuteStmt{Name: $2.(string)}
	}
|	"EXECUTE" Identifier "USING" UserVariableList
	{
		$$ = &stmts.ExecuteStmt{
			Name: $2.(string), 
			UsingVars: $4.([]expression.Expression),
		}
	}

UserVariableList:
	UserVariable
	{
		$$ = []expression.Expression{$1.(expression.Expression)}		
	}
|	UserVariableList ',' UserVariable
	{
		$$ = append($1.([]expression.Expression), $3.(expression.Expression))		
	}

/*
 * See: https://dev.mysql.com/doc/refman/5.0/en/deallocate-prepare.html
 */

DeallocateStmt:
	DeallocateSym "PREPARE" Identifier
	{
		$$ = &stmts.DeallocateStmt{Name: $3.(string)}
	}

DeallocateSym:
	"DEALLOCATE" | "DROP"

/****************************Prepared Statement End*******************************/


RollbackStmt:
	"ROLLBACK"
	{
		$$ = &stmts.RollbackStmt{}
	}

SelectStmt:
	"SELECT" SelectStmtOpts SelectStmtFieldList SelectStmtLimit SelectLockOpt
	{
		$$ = &stmts.SelectStmt {
			Distinct:      $2.(bool),
			Fields:        $3.([]*field.Field),
			From:          nil,
			Lock:	$5.(coldef.LockType),
		}
	}
|	"SELECT" SelectStmtOpts SelectStmtFieldList "FROM" 
	FromClause SelectStmtWhere SelectStmtGroup HavingClause SelectStmtOrder
	SelectStmtLimit SelectLockOpt
	{
		st := &stmts.SelectStmt{
			Distinct:	$2.(bool),
			Fields:		$3.([]*field.Field),
			From:		$5.(*rsets.JoinRset),
			Lock:		$11.(coldef.LockType),
		}

		if $6 != nil {
			st.Where = $6.(*rsets.WhereRset)
		}

		if $7 != nil {
			st.GroupBy = $7.(*rsets.GroupByRset)
		}

		if $8 != nil {
			st.Having = $8.(*rsets.HavingRset)
		}

		if $9 != nil {
			st.OrderBy = $9.(*rsets.OrderByRset)
		}

		if $10 != nil {
			ay := $10.([]interface{})
			st.Limit = ay[0].(*rsets.LimitRset)
			st.Offset = ay[1].(*rsets.OffsetRset)
		}
		
		$$ = st
	}

FromClause:
	TableRefs
	{
		$$ = $1
	}

TableRefs:
	EscapedTableRef
	{
		r := &rsets.JoinRset{Left: $1, Right: nil}
		if j, ok := $1.(*rsets.JoinRset); ok {
			// if $1 is JoinRset, use it directly
			r = j
		} 
		$$ = r
	}
|	TableRefs ',' EscapedTableRef
	{
		/* from a, b is default cross join */
		$$ = &rsets.JoinRset{Left: $1, Right: $3, Type: rsets.CrossJoin}
	}

EscapedTableRef:
	TableRef
	{
		$$ = $1
	}
|	'{' Identifier TableRef '}'
	{
		/* 
		* ODBC escape syntax for outer join is { OJ join_table } 
		* Use an Identifier for OJ 
		*/
		$$ = $3
	}

TableRef:
	TableFactor 
	{
		$$ = $1
	}
|	JoinTable
	{
		$$ = $1
	}

TableFactor:
	TableIdent TableIdentOpt
	{
		$$ = &rsets.TableSource{Source: $1, Name: $2.(string)}
	}
|	'(' SelectStmt semiOpt ')' AsOpt
	{
		$$ = &rsets.TableSource{Source: $2, Name: $5.(string)}
	}
|	'(' TableRefs ')'
	{
		$$ = $2
	}

TableIdentOpt:
	{
		$$ = ""
	}
|	AsOpt 
	{
		$$ = $1
	}


JoinTable:
	/* Use %prec to evaluate production TableRef before cross join */
	TableRef CrossOpt "JOIN" TableRef %prec tableRefPriority
	{
		$$ = &rsets.JoinRset{Left: $1, Right: $4, Type: rsets.CrossJoin}
	}
|	TableRef CrossOpt "JOIN" TableRef "ON" Expression
	{
		$$ = &rsets.JoinRset{Left: $1, Right: $4, Type: rsets.CrossJoin, On: $6.(expression.Expression)}
	}
|	TableRef JoinType OuterOpt "JOIN" TableRef "ON" Expression
	{	
		$$ = &rsets.JoinRset{Left: $1, Right: $5, Type: $2.(rsets.JoinType), On: $7.(expression.Expression)}
	} 
	/* Support Using */

JoinType:
	"LEFT"
	{
		$$ = rsets.LeftJoin
	}
|	"RIGHT"
	{
		$$ = rsets.RightJoin
	}
|	"FULL"
	{
		$$ = rsets.FullJoin
	}

OuterOpt:
	{
		$$ = nil
	}
|	"OUTER"


CrossOpt:
	{

	}
|	"CROSS" 
|	"INNER"


LimitClause:
	{
		$$ = (*rsets.LimitRset)(nil)
	}
|	"LIMIT" LengthNum
	{
		$$ = &rsets.LimitRset{Count: $2.(uint64)}
	}

SelectStmtLimit:
	{
		$$ = nil
	}
|	"LIMIT" LengthNum 
	{
		$$ = []interface{}{
			&rsets.LimitRset{Count: $2.(uint64)},
			(*rsets.OffsetRset)(nil)}
	}
|	"LIMIT" LengthNum ',' LengthNum 
	{
		$$ = []interface{}{
			&rsets.LimitRset{Count: $4.(uint64)},
			&rsets.OffsetRset{Count: $2.(uint64)}}
	}
|	"LIMIT" LengthNum "OFFSET" LengthNum
	{
		$$ = []interface{}{
			&rsets.LimitRset{Count: $2.(uint64)},
			&rsets.OffsetRset{Count: $4.(uint64)}}
	}

SelectStmtDistinct:
	/* EMPTY */
	{
		$$ = false
	}
|	"ALL"
	{
		$$ = false
	}
|	"DISTINCT"
	{
		$$ = true
	}

SelectStmtOpts:
	SelectStmtDistinct SelectStmtCalcFoundRows
	{
		// TODO: return calc_found_rows opt and support more other options
		$$ = $1
	}

SelectStmtCalcFoundRows:
	{
		$$ = false
	}
|	"SQL_CALC_FOUND_ROWS"
	{
		$$ = true
	}

SelectStmtFieldList:
	FieldList CommaOpt
	{
		$$ = $1
	}

SelectStmtWhere:
	/* EMPTY */
	{
		$$ = nil
	}
|	WhereClause

SelectStmtGroup:
	/* EMPTY */
	{
		$$ = nil
	}
|	GroupByClause

SelectStmtOrder:
	/* EMPTY */
	{
		$$ = nil
	}
|	OrderBy


// See: https://dev.mysql.com/doc/refman/5.7/en/subqueries.html
SubSelect:
	'(' SelectStmt ')'
	{
		s := $2.(stmt.Statement)
		s.SetText(yylex.(*lexer).src[yyS[yypt - 1].col-1:yyS[yypt].col-1])
		$$ = &expressions.SubQuery{Stmt: s}
	}

// See: https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
SelectLockOpt:
	/* empty */
	{
		$$ = coldef.SelectLockNone 
	}
|	"FOR" "UPDATE"
	{
		$$ = coldef.SelectLockForUpdate
	}
|	"LOCK" "IN" "SHARE" "MODE"
	{
		$$ = coldef.SelectLockInShareMode
	}

/********************Set Statement*******************************/
SetStmt:
	"SET" VariableAssignmentList
	{
		$$ = &stmts.SetStmt{Variables: $2.([]*stmts.VariableAssignment)}
	}
|	"SET" "NAMES" CharsetName
	{
		$$ = &stmts.SetCharsetStmt{Charset: $3.(string)} 
	}
|	"SET" "NAMES" CharsetName "COLLATE" CollationName
	{
		$$ = &stmts.SetCharsetStmt{
			Charset: $3.(string),
			Collate: $5.(string),
		} 
	}
|	"SET" CharsetKw CharsetName
	{
		$$ = &stmts.SetCharsetStmt{Charset: $3.(string)} 
	}
|	"SET" "PASSWORD" ForUserOpt eq PasswordOpt
	{
		$$ = &stmts.SetPwdStmt{User: $3.(string), Password: $5.(string)} 
	}

VariableAssignment:
	Identifier eq Expression
	{
		$$ = &stmts.VariableAssignment{Name: $1.(string), Value: $3.(expression.Expression), IsSystem: true}
	}
|	"GLOBAL" Identifier eq Expression
	{
		$$ = &stmts.VariableAssignment{Name: $2.(string), Value: $4.(expression.Expression), IsGlobal: true, IsSystem: true}
	}
|	"SESSION" Identifier eq Expression
	{
		$$ = &stmts.VariableAssignment{Name: $2.(string), Value: $4.(expression.Expression), IsSystem: true}
	}
|	"LOCAL" Identifier eq Expression
	{
		$$ = &stmts.VariableAssignment{Name: $2.(string), Value: $4.(expression.Expression), IsSystem: true}
	}
|	"SYS_VAR" eq Expression
	{
		v := strings.ToLower($1.(string))
		var isGlobal bool
		if strings.HasPrefix(v, "@@global.") {
			isGlobal = true
			v = strings.TrimPrefix(v, "@@global.")
		} else if strings.HasPrefix(v, "@@session.") {
			v = strings.TrimPrefix(v, "@@session.")
		} else if strings.HasPrefix(v, "@@local.") {
			v = strings.TrimPrefix(v, "@@local.")
		} else if strings.HasPrefix(v, "@@") {
			v = strings.TrimPrefix(v, "@@")
		}
		$$ = &stmts.VariableAssignment{Name: v, Value: $3.(expression.Expression), IsGlobal: isGlobal, IsSystem: true}
	}
|	"USER_VAR" eq Expression
	{
		v := $1.(string)
		v = strings.TrimPrefix(v, "@")
		$$ = &stmts.VariableAssignment{Name: v, Value: $3.(expression.Expression)}
	}

VariableAssignmentList:
	{
		$$ = []*stmts.VariableAssignment{} 
	}
|	VariableAssignment
	{
		$$ = []*stmts.VariableAssignment{$1.(*stmts.VariableAssignment)} 
	}
|	VariableAssignmentList ',' VariableAssignment
	{
		$$ = append($1.([]*stmts.VariableAssignment), $3.(*stmts.VariableAssignment)) 
	}

Variable:
	SystemVariable | UserVariable

SystemVariable:
	"SYS_VAR"
	{
		v := strings.ToLower($1.(string))
		var isGlobal bool
		if strings.HasPrefix(v, "@@global.") {
			isGlobal = true
			v = strings.TrimPrefix(v, "@@global.")
		} else if strings.HasPrefix(v, "@@session.") {
			v = strings.TrimPrefix(v, "@@session.")
		} else if strings.HasPrefix(v, "@@local.") {
			v = strings.TrimPrefix(v, "@@local.")
		} else if strings.HasPrefix(v, "@@") {
			v = strings.TrimPrefix(v, "@@")
		}
		$$ = &expressions.Variable{Name: v, IsGlobal: isGlobal, IsSystem: true}
	}

UserVariable:
	"USER_VAR"
	{
		v := $1.(string)
		v = strings.TrimPrefix(v, "@")
		$$ = &expressions.Variable{Name: v, IsGlobal: false, IsSystem: false}
	}


ForUserOpt:
	{
		$$ = "" 
	}
|	"FOR" Username
	{
		$$ = $2.(string)
	}

Username:
	Identifier
	{
		$$ = $1.(string)
	}

PasswordOpt:
	"PASSWORD" '(' AuthString ')' 
	{
		$$ = $3.(string)
	}

AuthString:
	Identifier
	{
		$$ = $1.(string)
	}

/****************************Show Statement*******************************/
ShowStmt:
	"SHOW" "ENGINES"
	{
		$$ = &stmts.ShowStmt{Target: stmt.ShowEngines}
	}
|	"SHOW" "DATABASES"
	{
		$$ = &stmts.ShowStmt{Target: stmt.ShowDatabases}
	}
|	"SHOW" "SCHEMAS"
	{
		$$ = &stmts.ShowStmt{Target: stmt.ShowDatabases}
	}
|	"SHOW" "CHARACTER" "SET"
	{
		$$ = &stmts.ShowStmt{Target: stmt.ShowCharset}
	}
|	"SHOW" "TABLES" ShowDatabaseNameOpt
	{
		$$ = &stmts.ShowStmt{
			Target: stmt.ShowTables,
			DBName: $3.(string)}
	}
|	"SHOW" "TABLES" ShowDatabaseNameOpt
	{
		$$ = &stmts.ShowStmt{
			Target: stmt.ShowTables,
			DBName: $3.(string)}
	}
|	"SHOW" OptFull "COLUMNS" ShowTableIdentOpt ShowDatabaseNameOpt
	{
		$$ = &stmts.ShowStmt{
			Target:     stmt.ShowColumns,
			TableIdent: $4.(table.Ident),
			DBName:     $5.(string),
			Full:	    $2.(bool),	
		}
	}
|	"SHOW" "WARNINGS"
	{
		$$ = &stmts.ShowStmt{Target: stmt.ShowWarnings}
	}

OptFull:
	{
		$$ = false
	}
|	"FULL"
	{
		$$ = true
	}

ShowDatabaseNameOpt:
	{
		$$ = ""
	}
|	"FROM" DBName
	{
		$$ = $2.(string)
	}
|	"IN" DBName
	{
		$$ = $2.(string)
	}

ShowTableIdentOpt:
	"FROM" TableIdent
	{
		$$ = $2.(table.Ident)
	}
|	"IN" TableIdent
	{
		$$ = $2.(table.Ident)
	}

Statement:
	EmptyStmt
|	AlterTableStmt
|	BeginTransactionStmt
|	CommitStmt
|	DeallocateStmt
|	DeleteFromStmt
|	ExecuteStmt
|	ExplainStmt
|	CreateDatabaseStmt
|	CreateIndexStmt
|	CreateTableStmt
|	DoStmt
|	DropDatabaseStmt
|	DropIndexStmt
|	DropTableStmt
|	InsertIntoStmt
|	PreparedStmt
|	RollbackStmt
|	SelectStmt
|	SetStmt
|	ShowStmt
|	TruncateTableStmt
|	UnionStmt
|	UpdateStmt
|	UseStmt

StatementList:
	Statement
	{
		if $1 != nil {
			s := $1.(stmt.Statement)
			s.SetText(yylex.(*lexer).stmtText())
			yylex.(*lexer).list = []stmt.Statement{ s }
		}
	}
|	StatementList ';' Statement
	{
		if $3 != nil {
			s := $3.(stmt.Statement)
			s.SetText(yylex.(*lexer).stmtText())
			yylex.(*lexer).list = append(yylex.(*lexer).list, s)
		}
	}

TableConstraint:
	"CONSTRAINT" name ConstraintElem
	{
		cst := $3.(*coldef.TableConstraint)
		cst.ConstrName = $2.(string)
		$$ = cst
	}
|	ConstraintElem 
	{
		$$ = $1
	}

TableElement:
	{
		$$ = nil
	}
|	ColumnDef
	{
		$$ = $1.(*coldef.ColumnDef)
	}
|	TableConstraint
	{
		$$ = $1.(*coldef.TableConstraint)
	}

TableElementList:
	{
		$$ = []interface{}{}
	}
|	TableElement
	{
		if $1 != nil {
			$$ = []interface{}{$1.(interface{})}
		} else {
			$$ = []interface{}{}
		}
	}
|	TableElementList ',' TableElement
	{
		if $3 != nil {
			$$ = append($1.([]interface{}), $3)
		} else {
			$$ = $1
		}
	}

TableOpt:
	"ENGINE" Identifier
	{
		$$ = &coldef.TableOpt{Tp: coldef.TblOptEngine, StrValue: $2.(string)} 
	}
|	"ENGINE" eq Identifier
	{
		$$ = &coldef.TableOpt{Tp: coldef.TblOptEngine, StrValue: $3.(string)} 
	}
|	DefaultKwdOpt CharsetKw EqOpt CharsetName
	{
		$$ = &coldef.TableOpt{Tp: coldef.TblOptCharset, StrValue: $4.(string)} 
	}
|	DefaultKwdOpt "COLLATE" EqOpt CollationName
	{
		$$ = &coldef.TableOpt{Tp: coldef.TblOptCollate, StrValue: $4.(string)} 
	}
|	"AUTO_INCREMENT" eq LengthNum
	{
		$$ = &coldef.TableOpt{Tp: coldef.TblOptAutoIncrement, UintValue: $3.(uint64)}
	}

TableOpts:
	{
		$$ = []*coldef.TableOpt{}
	}
|	TableOpt
	{
		$$ = []*coldef.TableOpt{$1.(*coldef.TableOpt)}
	}
|	TableOpts TableOpt
	{
		$$ = append($1.([]*coldef.TableOpt), $2.(*coldef.TableOpt))
	}
|	TableOpts ','  TableOpt
	{
		$$ = append($1.([]*coldef.TableOpt), $3.(*coldef.TableOpt))
	}


TruncateTableStmt:
	"TRUNCATE" "TABLE" TableIdent
	{
		$$ = &stmts.TruncateTableStmt{TableIdent: $3.(table.Ident)}
	}

/*************************************Type Begin***************************************/
Type:
	NumericType
	{
		$$ = $1
	}
|	StringType
	{
		$$ = $1
	}
|	DateAndTimeType
	{
		$$ = $1
	}
|	"duration"
	{
		x := types.NewFieldType($1.(byte))
		$$ = x
	}
|	"float32"
	{
		x := types.NewFieldType($1.(byte))
		$$ = x
	}
|	"float64"
	{
		x := types.NewFieldType($1.(byte))
		$$ = x
	}
|	"int64"
	{
		x := types.NewFieldType($1.(byte))
		$$ = x
	}
|	"string"
	{
		x := types.NewFieldType($1.(byte))
		$$ = x
	}
|	"uint"
	{
		x := types.NewFieldType($1.(byte))
		$$ = x
	}
|	"uint64"
	{
		x := types.NewFieldType($1.(byte))
		$$ = x
	}

NumericType:
	IntegerType OptFieldLen FieldOpts
	{
		// TODO: check flen 0
		x := types.NewFieldType($1.(byte))
		x.Flen = $2.(int)
		for _, o := range $3.([]*field.Opt) {
			if o.IsUnsigned {
				x.Flag |= mysql.UnsignedFlag
			}
			if o.IsZerofill {
				x.Flag |= mysql.ZerofillFlag
			}
		}
		$$ = x
	}
|	FixedPointType FloatOpt FieldOpts
	{
		fopt := $2.(*coldef.FloatOpt)
		x := types.NewFieldType($1.(byte))
		x.Flen = fopt.Flen 
		x.Decimal = fopt.Decimal
		for _, o := range $3.([]*field.Opt) {
			if o.IsUnsigned {
				x.Flag |= mysql.UnsignedFlag
			}
			if o.IsZerofill {
				x.Flag |= mysql.ZerofillFlag
			}
		}
		$$ = x
	}
|	FloatingPointType FloatOpt FieldOpts
	{
		fopt := $2.(*coldef.FloatOpt)
		x := types.NewFieldType($1.(byte))
		x.Flen = fopt.Flen 
		x.Decimal =fopt.Decimal
		for _, o := range $3.([]*field.Opt) {
			if o.IsUnsigned {
				x.Flag |= mysql.UnsignedFlag
			}
			if o.IsZerofill {
				x.Flag |= mysql.ZerofillFlag
			}
		}
		$$ = x
	}
|	BitValueType OptFieldLen
	{
		x := types.NewFieldType($1.(byte))
		x.Flen = $2.(int)
		$$ = x
	}

IntegerType:
	"TINYINT"
	{
		$$ = mysql.TypeTiny
	}
|	"SMALLINT"
	{
		$$ = mysql.TypeShort
	}
|	"MEDIUMINT"
	{
		$$ = mysql.TypeInt24
	}
|	"INT"
	{
		$$ = mysql.TypeLong 
	}
|	"INTEGER"
	{
		$$ = mysql.TypeLong
	}
|	"BIGINT"
	{
		$$ = mysql.TypeLonglong
	}
|	"BOOL"
	{
		$$ = mysql.TypeTiny        
	}
|	"BOOLEAN"
	{
		$$ = mysql.TypeTiny        
	}

OptInteger:
	{} | "INTEGER"

FixedPointType:
	"DECIMAL"
	{
		$$ = mysql.TypeNewDecimal
	}
|	"NUMERIC"
	{
		$$ = mysql.TypeNewDecimal
	}

FloatingPointType:
	"float"
	{
		$$ = mysql.TypeFloat
	}
|	"REAL"
	{
		$$ = mysql.TypeDouble
	}
|	"DOUBLE"
	{
		$$ = mysql.TypeDouble
	}
|	"DOUBLE" "PRECISION"
	{
		$$ = mysql.TypeDouble
	}

BitValueType:
	"BIT"
	{
		$$ = mysql.TypeBit 
	}

StringType:
	"CHAR" FieldLen OptBinary
	{
		x := types.NewFieldType(mysql.TypeString)
		x.Flen = $2.(int)
		if $3.(bool) {
			x.Flag |= mysql.BinaryFlag
		}
		$$ = x
	}
|	"CHAR" OptBinary
	{
		x := types.NewFieldType(mysql.TypeString)
		if $2.(bool) {
			x.Flag |= mysql.BinaryFlag
		}
		$$ = x
	}
|	"VARCHAR" FieldLen OptBinary OptCharset OptCollate
	{
		x := types.NewFieldType(mysql.TypeVarchar)
		x.Flen = $2.(int) 
		if $3.(bool) {
			x.Flag |= mysql.BinaryFlag
		}
		x.Charset = $4.(string)
		x.Collate = $5.(string)
		$$ = x
	}
|	"BINARY" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeString)
		x.Flen = $2.(int) 
		x.Charset = charset.CharsetBin 
		x.Collate = charset.CharsetBin
		$$ = x
	}
|	"VARBINARY" FieldLen
	{
		x := types.NewFieldType(mysql.TypeVarchar)
		x.Flen = $2.(int) 
		x.Charset = charset.CharsetBin 
		x.Collate = charset.CharsetBin
		$$ = x
	}
|	BlobType
	{
		$$ = $1.(*types.FieldType)
	}
|	TextType OptBinary OptCharset OptCollate
	{
		x := $1.(*types.FieldType)
		if $2.(bool) {
			x.Flag |= mysql.BinaryFlag
		}
		x.Charset = $3.(string)
		x.Collate = $4.(string)
		$$ = x
	}

BlobType:
	"TINYBLOB"
	{
		x := types.NewFieldType(mysql.TypeTinyBlob)
		x.Charset = charset.CharsetBin
		x.Collate = charset.CharsetBin
		$$ = x
	}
|	"BLOB"
	{
		x := types.NewFieldType(mysql.TypeBlob)
		x.Charset = charset.CharsetBin
		x.Collate = charset.CharsetBin
		$$ = x
	}
|	"MEDIUMBLOB"
	{
		x := types.NewFieldType(mysql.TypeMediumBlob)
		x.Charset = charset.CharsetBin
		x.Collate = charset.CharsetBin
		$$ = x
	}
|	"LONGBLOB"
	{
		x := types.NewFieldType(mysql.TypeLongBlob)
		x.Charset = charset.CharsetBin
		x.Collate = charset.CharsetBin
		$$ = x
	}

TextType:
	"TINYTEXT"
	{
		x := types.NewFieldType(mysql.TypeTinyBlob)
		$$ = x

	}
|	"TEXT"
	{
		x := types.NewFieldType(mysql.TypeBlob)
		$$ = x
	}
|	"MEDIUMTEXT"
	{
		x := types.NewFieldType(mysql.TypeMediumBlob)
		$$ = x
	}
|	"LONGTEXT"
	{
		x := types.NewFieldType(mysql.TypeLongBlob)
		$$ = x
	}


DateAndTimeType:
	"DATE"
	{
		x := types.NewFieldType(mysql.TypeDate)
		$$ = x
	}
|	"DATETIME" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeDatetime)
		x.Decimal = $2.(int)
		$$ = x
	}
|	"TIMESTAMP" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeTimestamp)
		x.Decimal = $2.(int)
		$$ = x
	}
|	"TIME" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeDuration)
		x.Decimal = $2.(int)
		$$ = x
	}
|	"YEAR"
	{
		x := types.NewFieldType(mysql.TypeYear)
		$$ = x
	}

FieldLen:
	'(' LengthNum ')'
	{
		$$ = int($2.(uint64))
	}

OptFieldLen:
	{
		/* -1 means unspecified field length*/
		$$ = types.UnspecifiedLength 
	}
|	FieldLen
	{   
		$$ = $1.(int)
	}

FieldOpt:
	"UNSIGNED"
	{
		$$ = &field.Opt{IsUnsigned: true}
	}
|	"ZEROFILL"
	{
		$$ = &field.Opt{IsZerofill: true, IsUnsigned: true}
	}

FieldOpts:
	{
		$$ = []*field.Opt{}
	}
|	FieldOpt
	{
		$$ = []*field.Opt{$1.(*field.Opt)}    
	}
|	FieldOpts FieldOpt
	{
		$$ = append($1.([]*field.Opt), $2.(*field.Opt)) 
	}

FloatOpt:
	{
		$$ = &coldef.FloatOpt{Flen: types.UnspecifiedLength, Decimal: types.UnspecifiedLength}
	}
|	FieldLen
	{
		$$ = &coldef.FloatOpt{Flen: $1.(int), Decimal: types.UnspecifiedLength}
	}
|	Precision
	{
		$$ = $1.(*coldef.FloatOpt)
	}

Precision:
	'(' LengthNum ',' LengthNum ')'
	{
		$$ = &coldef.FloatOpt{Flen: int($2.(uint64)), Decimal: int($4.(uint64))}
	}

OptBinary:
	{
		$$ = false
	}
|	"BINARY"
	{
		$$ = true
	}

OptCharset:
	{
		$$ = ""
	}
|	CharsetKw CharsetName
	{
		$$ = $2.(string)
	}

CharsetKw:
	"CHARACTER" "SET"
|	"CHARSET"

OptCollate:
	{
		$$ = ""
	}
|	"COLLATE" CollationName
	{
		$$ = $2.(string)
	}

/************************************************************************************
 * Union statement
 * See: https://dev.mysql.com/doc/refman/5.7/en/union.html
 ***********************************************************************************/
UnionStmt:
	SelectStmt "UNION" UnionOpt SelectStmt
	{
		ds := []bool {$3.(bool)}
		ss := []*stmts.SelectStmt{$1.(*stmts.SelectStmt), $4.(*stmts.SelectStmt)}
		$$ = &stmts.UnionStmt{
			Distincts:	ds,
			Selects:	ss,
		}
	}
|	UnionStmt "UNION" UnionOpt SelectStmt
	{
		s := $1.(*stmts.UnionStmt)
		s.Distincts = append(s.Distincts, $3.(bool))
		s.Selects = append(s.Selects, $4.(*stmts.SelectStmt))
		$$ = s	
	}

UnionOpt:
	{
		$$ = true
	}
|	"ALL"
	{
		$$ = false
	}
|	"DISTINCT"
	{
		$$ = true
	}


/************************************************************************************/
UpdateStmt:
	"UPDATE" LowPriorityOptional IgnoreOptional TableIdent SetOpt AssignmentList WhereClauseOptional OrderByOptional LimitClause
	{
		var expr expression.Expression
		if w := $7; w != nil {
			expr = w.(*rsets.WhereRset).Expr
		}
		st := &stmts.UpdateStmt{
			LowPriority:    $2.(bool),
			TableIdent:     $4.(table.Ident),
			List:           $6.([]expressions.Assignment), 
			Where:          expr} 
	
		if $8 != nil {
			 st.Order = $8.(*rsets.OrderByRset)
		}
		if $9 != nil {
			st.Limit = $9.(*rsets.LimitRset)
		}
		$$ = st
		if yylex.(*lexer).root {
			break
		}
	}


UseStmt:
	"USE" DBName
	{
		$$ = &stmts.UseStmt{DBName: $2.(string)} 
		if yylex.(*lexer).root {
			break
		}
	}

WhereClause:
	"WHERE" Expression
	{
		$$ = &rsets.WhereRset{Expr: expressions.Expr($2)}
	}

WhereClauseOptional:
	{
		$$ = nil
	}
|	WhereClause
	{
		$$ = $1
	}

SetOpt:
	{
	}
|	"SET"
	{
	}

CommaOpt:
	{
	}
|	','
	{
	}

%%

